package com.findme.test.xml;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Element;
import org.springframework.lang.NonNull;

/**
 * @ClassName: XMLUtil
 * @description: XMLUtil
 * @author: find me
 * @create: 2022-02-17 15:15
 * @Version 1.0
 */
public class XMLUtil {

    /**
     * XML 转 json （fastjson）
     */
    public static void xmlToJson(Element element, JSONObject json) {
        // 获取所有节点
        List<Element> chdEl = element.elements();
        //如果没有子元素,只有一个值
        if (chdEl.isEmpty() && !StringUtils.isEmpty(element.getText())) {
            json.put(element.getName(), element.getText());
        }
        //有子元素
        for (Element e : chdEl) {
            //子元素也有子元素
            if (!e.elements().isEmpty()) {
                JSONObject chdjson = new JSONObject();
                xmlToJson(e, chdjson);
                Object o = json.get(e.getName());
                if (o != null) {
                    JSONArray jsona = null;
                    //如果此元素已存在,则转为jsonArray
                    if (o instanceof JSONObject) {
                        JSONObject jsono = (JSONObject) o;
                        json.remove(e.getName());
                        jsona = new JSONArray();
                        jsona.add(jsono);
                        jsona.add(chdjson);
                    }
                    if (o instanceof JSONArray) {
                        jsona = (JSONArray) o;
                        jsona.add(chdjson);
                    }
                    json.put(e.getName(), jsona);
                } else {
                    if (!chdjson.isEmpty()) {
                        json.put(e.getName(), chdjson);
                    }
                }

            } else {//子元素没有子元素

                if (!e.getText().isEmpty()) {
                    json.put(e.getName(), e.getText());
                }
            }
        }
    }

    /**
     * xml 转 jsonArray
     *
     * @param elements list 的 dom
     * @param jsonArray 空jsonArray
     */
    public static void xmlToJson(List<Element> elements, JSONArray jsonArray) {
        for (Element element : elements) {
            JSONObject json = new JSONObject();
            xmlToJson(element, json);
            jsonArray.add(json);
        }
    }


    /**
     * 实体类转xml
     *
     * @param object 实体类数据
     * @param rootDom xml文档
     * @param initialName 初始节点的 名称，可为空 可通过是否为空，手动添加是否需要增加父节点
     */
    public static void entityTransferToXml(Object object, Element rootDom, String initialName) {
        try {
            // 创建根节点元素 (是否需要根节点为报文实体类的名称)
            // 获取类的简写名称
            Element entityDom = rootDom;
            if (StringUtils.isNotBlank(initialName)) {
                entityDom = entityDom.addElement(initialName);
            }
            // 获取实体类b的所有属性，返回Field数组
            Field[] field = object.getClass().getDeclaredFields();
            // 遍历所有有属性
            for (int j = 0; j < field.length; j++) {
                // 获取属性的名字
                String fieldName = field[j].getName();
                //去除串行化序列属性  field[j].isSynthetic() 处理非原生字段 针对 $jacocoData合成字段
                if (!fieldName.equals("serialVersionUID") && !field[j].isSynthetic()) {
                    // 将属性的首字符大写，方便构造get，set方法
                    fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //    System.out.println("属性get"+name+"方法返回值类型:" + m.getReturnType());
                    Method m = object.getClass().getMethod("get" + fieldName);
                    // 获取字段的值
                    Object propertiesValue = m.invoke(object);
                    // 获取数据不为空
                    if (null != propertiesValue) {
                        // 判断是否是 是基本数据类型 或者  BigDecimal
                        if (m.getReturnType().toString().contains("java.lang") || m.getReturnType()
                                .equals(BigDecimal.class)
                                || isBaseDefaultValue(m.getReturnType())) {
                            Element properties = entityDom.addElement(fieldName);
                            properties.setText(propertiesValue.toString());
                            // 如果是list
                        } else if (List.class.equals(m.getReturnType())) {
                            entityTransferToXml((List<Object>) propertiesValue, entityDom,
                                    fieldName);
                        } else {
                            //Element propertie = entity.addElement(name);
                            // 如果是新的对象
                            entityTransferToXml(propertiesValue, entityDom,
                                    fieldName);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * list实体类转xml 如果有多个节点，xml文档必定要以 节点名称做区分
     *
     * @param objects 实体类数据
     * @param rootDom, element 文档
     * @param nodeName dom节点名称
     * @param <T> 实体类泛型
     */
    public static <T> void entityTransferToXml(List<T> objects, Element rootDom,
            @NonNull String nodeName) {
        for (Object object : objects) {
            Element entitySet = rootDom.addElement(nodeName);
            entityTransferToXml(object, entitySet, null);
        }
    }


    /**
     * xml转实体类
     *
     * @param rootElt dom 节点
     * @param pojo 数据类型
     */
    public static <T> T fromXmlToBean(Element rootElt, Class<T> pojo) throws Exception {
        // 首先得到pojo所定义的字段
        Field[] fields = pojo.getDeclaredFields();
        // 根据传入的Class动态生成pojo对象
        T obj = pojo.newInstance();
        for (Field field : fields) {
            // 设置字段可访问（必须，否则报错）
            field.setAccessible(true);
            // 得到字段的属性名
            String name = field.getName();
            // 这一段的作用是如果字段在Element中不存在会抛出异常，如果出异常，则跳过。
            name = name.substring(0, 1).toUpperCase() + name.substring(1);
            // 1.如果是基本数据类型 2基本数据类型的包装类型，3.BigDecimal
            if (field.getType().toString().contains("java.lang") || field.getType()
                    .equals(BigDecimal.class)
                    || isBaseDefaultValue(field.getType())) {
                // 根据字段的类型将值转化为相应的类型，并设置到生成的对象中
                if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
                    field.set(obj, Long.parseLong(rootElt.elementTextTrim(name)));
                } else if (field.getType().equals(String.class)) {
                    field.set(obj, rootElt.elementTextTrim(name));
                } else if (field.getType().equals(Double.class) || field.getType()
                        .equals(double.class)) {
                    field.set(obj, Double.parseDouble(rootElt.elementTextTrim(name)));
                } else if (field.getType().equals(Integer.class) || field.getType()
                        .equals(int.class)) {
                    field.set(obj, Integer.parseInt(rootElt.elementTextTrim(name)));
                } else if (field.getType().equals(BigDecimal.class)) {
                    field.set(obj, new BigDecimal(rootElt.elementTextTrim(name)));
                } else {
                    field.set(obj, String.valueOf(rootElt.elementTextTrim(name)));
                }
            } else if (List.class.equals(field.getType())) {
                List<Element> elements = rootElt.elements(name);
                //((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0].getTypeName().getClass();
                Class<?> aClass = getSingleGenericClassNameList(field);
                List<?> objects = fromXmlToBean(elements, aClass);
                field.set(obj, objects);
            } else {
                Method m = obj.getClass().getMethod("get" + name);
                Object o = fromXmlToBean(rootElt.element(name), m.getReturnType());
                field.set(obj, o);
            }

        }
        return obj;
    }

    /**
     * xml 转list
     *
     * @param rootElt list的dom节点
     * @param pojo 承载数据的list类型
     */
    public static <T> List<T> fromXmlToBean(List<Element> rootElt, Class<T> pojo) throws Exception {
        List<T> objects = new ArrayList<>();
        for (Element element : rootElt) {
            T o = fromXmlToBean(element, pojo);
            objects.add(o);
        }
        return objects;

    }


    /**
     * 判断是否是基础类型
     *
     * @param classType 字段类型 class
     */
    public static boolean isBaseDefaultValue(Class classType) {
        if (classType.equals(int.class) || classType.equals(byte.class)
                || classType.equals(long.class) || classType.equals(double.class)
                || classType.equals(float.class) || classType.equals(char.class)
                || classType.equals(short.class) || classType.equals(boolean.class)) {
            return true;
        }
        return false;
    }


    /**
     * 获取list中的泛型类型 例如 List<?> 取出？的class
     *
     * @param field 字段
     */
    public static Class<?> getSingleGenericClassNameList(Field field) {
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            //得到泛型里的class类型对象
            Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
            return genericClazz;
        }
        throw new NullPointerException(String.format("Target %s can't find generic",
                field.getDeclaringClass().getName() + "." + field.getName()));

    }

    //    /**
//     * xml转实体类 （速度过慢）
//     *
//     * @param rootElt
//     * @param pojo
//     * @return
//     * @throws Exception
//     */
//    public static Object reflectXmlToBean(Element rootElt, Class pojo) throws Exception {
//        // 首先得到pojo所定义的字段
//        Field[] fields = pojo.getDeclaredFields();
//        Map<String, Field> fieldMap = Arrays.stream(fields).collect(Collectors.toMap(item -> (
//                item.getName().substring(0, 1).toUpperCase() + item.getName().substring(1)), item -> item, (V1, V2) -> V2));
//
//        // 根据传入的Class动态生成pojo对象
//        Object obj = pojo.newInstance();
//        Iterator<Element> elementIterator = rootElt.elementIterator();
//        while (elementIterator.hasNext()) {
//            Element element = elementIterator.next();
//            String elementName = element.getName();
//            if (fieldMap.containsKey(elementName)) {
//                Field field = fieldMap.get(elementName);
//                // 设置字段可访问（必须，否则报错）
//                field.setAccessible(true);
//                if (field.getType().toString().contains("java.lang") || field.getType().equals(java.math.BigDecimal.class)
//                        || isBaseDefaultValue(field.getType())) {
//                    if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
//                        field.set(obj, Long.parseLong(element.getText()));
//                    } else if (field.getType().equals(String.class)) {
//                        field.set(obj, element.getText());
//                    } else if (field.getType().equals(Double.class) || field.getType().equals(double.class)) {
//                        field.set(obj, Double.parseDouble(element.getText()));
//                    } else if (field.getType().equals(Integer.class) || field.getType().equals(int.class)) {
//                        field.set(obj, Integer.parseInt(element.getText()));
//                    } else if (field.getType().equals(BigDecimal.class)) {
//                        field.set(obj, new BigDecimal(element.getText()));
//                    } else {
//                        field.set(obj, element.getText());
//                    }
//                } else if (java.util.List.class.equals(field.getType())) {
//                    List<Element> elements = rootElt.elements(elementName);
////                    Class<?> aClass = (Class<?>) (((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0]);
//                    Class<?> aClass = getSingleGenericClassNameList(field);
//                    List<?> objects = reflectXmlToBean(elements, aClass);
//                    field.set(obj, objects);
//                } else {
//                    Method m = obj.getClass().getMethod("get" + elementName);
//                    Object o = reflectXmlToBean(rootElt.element(elementName), m.getReturnType());
//                    field.set(obj, o);
//                }
//
//            }
//        }
//
//        return obj;
//    }

//    /**
//     * xml 转 list （速度过慢）
//     *
//     * @param rootElt
//     * @param pojo
//     * @param <T>
//     * @return
//     * @throws Exception
//     */
//    public static <T> List<T> reflectXmlToBean(List<Element> rootElt, Class<T> pojo) throws Exception {
//        List<T> objects = new ArrayList<>();
//        for (Element element : rootElt) {
//            T o = (T) (reflectXmlToBean(element, pojo));
//            objects.add(o);
//        }
//        return objects;
//    }
}
